package org.newtech.objectgrid.examples;


import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;

import java.util.HashMap;

import org.newtech.objectgrid.examples.Customer;

import com.ibm.websphere.objectgrid.plugins.Loader;
import com.ibm.websphere.objectgrid.BackingMap;
import com.ibm.websphere.objectgrid.ObjectMap;
import com.ibm.websphere.objectgrid.Session;
import com.ibm.websphere.objectgrid.TxID;
import com.ibm.websphere.objectgrid.plugins.LoaderException;
import com.ibm.websphere.objectgrid.plugins.LogElement;
import com.ibm.websphere.objectgrid.plugins.LogSequence;
import com.ibm.websphere.objectgrid.plugins.ObjectTransformer;
import com.ibm.websphere.objectgrid.plugins.OptimisticCallback;
import com.ibm.websphere.objectgrid.plugins.OptimisticCollisionException;

import com.ibm.websphere.projector.Tuple;
import com.ibm.websphere.projector.md.TupleMetadata;
import com.ibm.websphere.projector.md.EntityMetadata;
import com.ibm.websphere.projector.md.TupleAssociation;


/**
 */
public class CustomerLoader implements Loader {

	private EntityMetadata entityMetaData;
    
    private static final String DB_DRIVER = "com.mysql.jdbc.Driver";
    private static final String DB_CONNECTION_URL = "jdbc:mysql://localhost:3306/test";
    private static final String DB_USER = "root";
    private static final String DB_PASSWORD = "";

    protected String  ivBackingMapName;
    
    public void batchUpdate(TxID txid, LogSequence sequence)
        throws LoaderException, OptimisticCollisionException {
        Iterator iter = sequence.getPendingChanges();
        while (iter.hasNext()) {
            LogElement logElement = (LogElement) iter.next();
            Object key = logElement.getCacheEntry().getKey();
            Object value = logElement.getCurrentValue();

            switch (logElement.getType().getCode()) {
            case LogElement.CODE_INSERT:
                if (entityMetaData!=null) {
                    // The order has just one key orderNumber
                    String ORDERNUMBER=(String) getKeyAttribute("orderNumber", (Tuple) key);
                    // Get the value of date
                    java.util.Date unFormattedDate = (java.util.Date) getValueAttribute("date",(Tuple)value);
                    // The values are 2 associations. Lets process customer because
                    // the our table contains customer.id as primary key
                    Object[] keys= getForeignKeyForValueAssociation("customer","id",(Tuple) value);

                    //Order to Customer is M to 1. There can only be 1 key
                    String CUSTOMER_ID=(String)keys[0];

                    // parse variable unFormattedDate and format it for the database as formattedDate
                    String formattedDate = "2007-05-08-14.01.59.780272"; // formatted for DB2

                    // Finally, the following SQL statement to insert the record
                    //INSERT INTO ORDER (ORDERNUMBER, DATE, CUSTOMER_ID) VALUES(ORDERNUMBER,formattedDate, CUSTOMER_ID)
                }
                break;
            case LogElement.CODE_UPDATE:
                break;
            case LogElement.CODE_DELETE:
                break;
            }
        }

    }

    public void preloadMap(Session session, BackingMap backingMap) throws LoaderException {
		this.entityMetaData=backingMap.getEntityMetadata();
	}


    // returns the value to attribute as stored in the key Tuple
    private Object getKeyAttribute(String attr, Tuple key) {
        //get key metadata
        TupleMetadata keyMD = entityMetaData.getKeyMetadata();
        //get position of the attribute
        int keyAt = keyMD.getAttributePosition(attr);
        if (keyAt > -1) {
            return key.getAttribute(keyAt);
        } else { // attribute undefined
            throw new IllegalArgumentException("Invalid position index for  "+attr);
        }

    }
    // returns the value to attribute as stored in the value Tuple
    private Object getValueAttribute(String attr, Tuple value) {
        //similar to above, except we work with value metadata instead
        TupleMetadata valueMD = entityMetaData.getValueMetadata();

        int keyAt = valueMD.getAttributePosition(attr);
        if (keyAt > -1) {
            return value.getAttribute(keyAt);
        } else {
            throw new IllegalArgumentException("Invalid position index for  "+attr);
        }
    }
    // returns an array of keys that refer to association.
    private Object[] getForeignKeyForValueAssociation(String attr, String fk_attr, Tuple value) {
        TupleMetadata valueMD = entityMetaData.getValueMetadata();
        Object[] ro;

        int customerAssociation = valueMD.getAssociationPosition(attr);
        TupleAssociation tupleAssociation = valueMD.getAssociation(customerAssociation);

        EntityMetadata targetEntityMetaData = tupleAssociation.getTargetEntityMetadata();

        Tuple[] customerKeyTuple = ((Tuple) value).getAssociations(customerAssociation);

        int numberOfKeys = customerKeyTuple.length;
        ro = new Object[numberOfKeys];

        TupleMetadata keyMD = targetEntityMetaData.getKeyMetadata();
        int keyAt = keyMD.getAttributePosition(fk_attr);
        if (keyAt < 0) {
            throw new IllegalArgumentException("Invalid position index for  " + attr);
        }
        for (int i = 0; i < numberOfKeys; ++i) {
            ro[i] = customerKeyTuple[i].getAttribute(keyAt);
        }
        return ro;

    }

    public List get(TxID txid, List keyList, boolean forUpdate)	throws LoaderException {
		System.out.println("OrderLoader: Get called");
		ArrayList returnList = new ArrayList();

		if (entityMetaData != null) {
			int index=0;
			for (Iterator iter = keyList.iterator(); iter.hasNext();) {
				Tuple orderKeyTuple=(Tuple) iter.next();
                
				// The order has just one key orderNumber
				String ORDERNUMBERKEY = (String) getKeyAttribute("orderNumber",orderKeyTuple);
				//We need to run a query to get values of
				// SELECT CUSTOMER_ID, date FROM ORDER WHERE ORDERNUMBER='ORDERNUMBERKEY'
                
				//1) Foreign key: CUSTOMER_ID
				//2) date
				// Assuming those two are returned as
                String  CUSTOMER_ID = "C001"; // Assuming Retrieved and initialized
                java.util.Date retrievedDate = new java.util.Date(); // Assuming this date reflects the one in database
                // We now need to convert this data into a tuple before returning

                //create a value tuple
                TupleMetadata valueMD = entityMetaData.getValueMetadata();
                Tuple valueTuple=valueMD.createTuple();


				//add retrievedDate object to Tuple
				int datePosition = valueMD.getAttributePosition("date");
				valueTuple.setAttribute(datePosition, retrievedDate);

				//Next need to add the Association
				int customerPosition=valueMD.getAssociationPosition("customer");
				TupleAssociation customerTupleAssociation = valueMD.getAssociation(customerPosition);
				EntityMetadata customerEMD = customerTupleAssociation.getTargetEntityMetadata();
				TupleMetadata customerTupleMDForKEY=customerEMD.getKeyMetadata();
				int customerKeyAt=customerTupleMDForKEY.getAttributePosition("id");


				Tuple customerKeyTuple=customerTupleMDForKEY.createTuple();
				customerKeyTuple.setAttribute(customerKeyAt, CUSTOMER_ID);
				valueTuple.addAssociationKeys(customerPosition, new Tuple[] {customerKeyTuple});


				int linesPosition = valueMD.getAssociationPosition("lines");
				TupleAssociation linesTupleAssociation = valueMD.getAssociation(linesPosition);
				EntityMetadata orderLineEMD = linesTupleAssociation.getTargetEntityMetadata();
				TupleMetadata orderLineTupleMDForKEY = orderLineEMD.getKeyMetadata();
				int lineNumberAt = orderLineTupleMDForKEY.getAttributePosition("lineNumber");
				int orderAt = orderLineTupleMDForKEY.getAssociationPosition("order");

				if (lineNumberAt < 0 || orderAt < 0) {
					throw new IllegalArgumentException(
                        "Invalid position index for lineNumber or order "+ lineNumberAt + " " + orderAt);
				}
                // SELECT LINENUMBER FROM ORDERLINE WHERE ORDERNUMBER='ORDERNUMBERKEY'
				// Assuming two rows of line number are returned with values 1 and 2

				Tuple orderLineKeyTuple1 = orderLineTupleMDForKEY.createTuple();
				orderLineKeyTuple1.setAttribute(lineNumberAt, new Integer(1));// set Key
				orderLineKeyTuple1.addAssociationKey(orderAt, orderKeyTuple);

				Tuple orderLineKeyTuple2 = orderLineTupleMDForKEY.createTuple();
				orderLineKeyTuple2.setAttribute(lineNumberAt, new Integer(2));// Init Key
				orderLineKeyTuple2.addAssociationKey(orderAt, orderKeyTuple);

				valueTuple.addAssociationKeys(linesPosition, new Tuple[] {orderLineKeyTuple1, orderLineKeyTuple2 });

				returnList.add(index, valueTuple);

				index++;

			}
		}else {
			// does not support tuples
		}
		return returnList;
	}
    
    /*
     *  (non-Javadoc)
     * @see com.ibm.websphere.samples.objectgrid.HeapLoader#getBackingMapName()
     */
    public String getBackingMapName()
    {
        return ivBackingMapName;
    }

    public synchronized void commit(TxID tx, HashMap changes) {
    }

    public static Connection getDBConnection() throws Exception {
        Class.forName(DB_DRIVER);
        return DriverManager.getConnection(DB_CONNECTION_URL, DB_USER, DB_PASSWORD);
    }
    
    public static void closeDBConnection(Connection conn) {
        try {
            if (conn != null && !conn.isClosed()) {
                conn.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }



}


